/*
 * WaveView.java
 * 
 * last update: 14.01.2010 by Stefan Saru
 * 
 * author:	Lucian Ghita Felix(felix@ulise.cs.pub.ro)
 *
 * Obs: N/A
 */

package ide;

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import middle.*;
import javax.swing.event.ChangeListener;

@SuppressWarnings("serial")
public class WaveView extends JPanel implements middle.TimeUnitChangeListener {
	public JList waveList = new JList();                //contzine graficele variabilelor urmarite
	public JList waveNameList = new JList();            //contzine numele variabilelor urmarite
	//private JScrollBar scrollBar;

	//public long simulationTime = 0;   
	//nu pot face o interfata grafica care sa foloseasca o valoare long.
	//mi-ar trebui cel putin un scrollBar care sa tina o valoare long.
	//ashi putea tine in scrollBar   simulationTime / 10
	//...in versiunea 2.0
	//nu incercati simulari lungi...:-<

	public int simulationTime = 0;

	public JScrollPane waveScroller;
	public WaveNameScroller waveNameScroller;
	public TimeHeader timeHeader;                       //afisheaza ccordonata timp

	public static final int waveHeight = 40;           //inaltzimea unei componente din lista
	public static final int waveMinimumUnitWeigth = 10; //latzime time unit cand zoom-ul e 0
	public static final int zoomIncrement = 10;         //cu cat creshte(scade) zoom-ul
	public static final int zoomLimit = 70;             //zoom maxim

	public int actualZoom = 10;
	public int displayStartTime = 0;                    //timeUnit-ul din stanga graficului

	public Project project;

	/**
	 * If true, the waveView should automatically scroll to the current time unit.
	 * (!only! if the horizontal scrollbar's position is to the rightmost position)
	 */
	boolean autoHScroll;

	//latzimea unui time unit(in pixeli) = waveMinimumUnitWeigth + actualZoom;

	//veriabilele legate de linia verticala
	int xPos;
	boolean isLine = false;




	public WaveView(Project project) { //constructorul va avea  parametrii. (care?)
		setLayout(new BorderLayout());
		this.project = project;
		vide.spl.showStatus("Creating WaveView component..");

		waveList.setCellRenderer(new WaveListCellRenderer(this));
		waveList.setModel(new DefaultListModel());
		waveList.setFixedCellHeight(waveHeight);
		waveList.setBackground(Color.black);
		waveList.setBorder(new javax.swing.border.BevelBorder(javax.swing.border.BevelBorder.LOWERED));

		waveNameList.setForeground(Color.white);
		waveNameList.setFixedCellHeight(waveHeight);
		waveNameList.setModel(new DefaultListModel());
		waveNameList.setCellRenderer(new DefaultListCellRenderer());
		//waveNameList.setFixedCellWidth(120);
		waveNameList.setBackground(Color.black);
		waveNameList.setBorder(new javax.swing.border.BevelBorder(javax.swing.border.BevelBorder.LOWERED));
		waveScroller = new JScrollPane(waveList);
		timeHeader = new TimeHeader();
		//waveNameScroller.setColumnHeaderView(waveNameScroller);
		autoHScroll = true;
		waveScroller.setColumnHeaderView(timeHeader);


		JScrollBar waveNameScrollBar = new JScrollBar(JScrollBar.HORIZONTAL);
		waveScroller.setCorner(JScrollPane.LOWER_LEFT_CORNER, waveNameScrollBar);
		JScrollPane nameScrollPane = new NameScrollPane(waveNameList);
		nameScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_NEVER);
		nameScrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
		//nameScrollPane.setHorizontalScrollBar(waveNameScrollBar);
		waveScroller.setRowHeaderView(nameScrollPane);
		/*    nameScrollPane.setMinimumSize(new Dimension(120, 0));
          nameScrollPane.setMaximumSize(new Dimension(120, 1000));*/
		waveNameScroller = new WaveNameScroller(waveNameScrollBar, nameScrollPane.getViewport());

		//waveNameScroller.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
		waveScroller.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
		waveScroller.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
		vide.spl.showStatus("Creating WaveView component..");
		add(waveScroller, BorderLayout.CENTER);

		vide.spl.showStatus("Creating WaveView component..done");

		MouseListener ml = new MouseAdapter() {
			public void mouseClicked(MouseEvent e) { 
				if(e.getModifiers() == InputEvent.BUTTON1_MASK){                    
					xPos = e.getX();
					isLine = true;  
					for(int index = 0; index < ((DefaultListModel) waveNameList.getModel()).getSize();index++){
						String name = (String)((DefaultListModel) waveNameList.getModel()).getElementAt(index);
						int width = waveMinimumUnitWeigth + actualZoom;
						VariableHistory vh = (VariableHistory)((DefaultListModel) waveList.getModel()).getElementAt(index);
						int n = name.indexOf(':');   
						if(n!=-1){
							name = name.substring(0,n-1);
						}
						name = name + " : " + vh.getStringValue(xPos / width);
						//name =  vh.getStringValue(xPos / width);
						((DefaultListModel) waveNameList.getModel()).setElementAt(name,index);
					} 
				}
				else if(e.getModifiers() == InputEvent.BUTTON3_MASK){
					isLine = false;
					for(int index = 0; index < ((DefaultListModel) waveNameList.getModel()).getSize();index++){
						String name = (String)((DefaultListModel) waveNameList.getModel()).getElementAt(index);
						int n = name.indexOf(':');
						if(n!=-1){
							name = name.substring(0,n-1);
						}
						((DefaultListModel) waveNameList.getModel()).setElementAt(name,index);
					}
				}              

				waveNameList.invalidate();
				waveNameList.repaint();

				waveList.invalidate();
				waveList.repaint();

			}
		};

		waveList.addMouseListener(ml);
	}

	public Object[] reset(){
		simulationTime = 0;
		return clear();
	}

	public Object[] clear() {
		Object [] data = ((DefaultListModel) waveNameList.getModel()).toArray();
		for(int i = 0; i < data.length; i++){ //remove " : <value>" from names:
			int n = ((String)data[i]).indexOf(':');   
			if(n!=-1){
				data[i] = ((String)data[i]).substring(0,n-1);
			}
		}
		((DefaultListModel) waveList.getModel()).clear();
		((DefaultListModel) waveNameList.getModel()).clear();
		waveList.invalidate();
		waveNameList.invalidate();

		waveList.repaint();
		waveNameList.repaint();
		project.saveWaveSetAction.setEnabled(false);

		return data;
	}

	public void addWave(VariableHistory vh, String name) {
		if(vh == null){
			throw new Error("WaveView.addWave : VarHist is null for fqn " + name);
		}
		((DefaultListModel) waveList.getModel()).addElement(vh);
		((DefaultListModel) waveNameList.getModel()).addElement(name);   //vh

		waveList.invalidate();
		waveNameList.invalidate();

		waveList.repaint();
		waveNameList.repaint();
		project.saveWaveSetAction.setEnabled(true);
	};

	public void removeWave() {
		int index = waveNameList.getSelectedIndex();
		if (index == -1) return;
		//daca nu e nimic selectat ar trebui ca actiunea sa fie disabled.
		((DefaultListModel) waveNameList.getModel()).removeElementAt(index);
		((DefaultListModel) waveList.getModel()).removeElementAt(index);

		waveList.invalidate();
		waveNameList.invalidate();

		/*waveList.repaint();
      waveNameList.repaint();*/
		if (((DefaultListModel)waveNameList.getModel()).getSize() == 0) project.saveWaveSetAction.setEnabled(false);
	};


	private void repackWaveList() {
		waveList.setFixedCellWidth((int)(actualZoom + waveMinimumUnitWeigth) *
				simulationTime);
		/*    if (autoHScroll){
      JScrollBar hScroll = waveScroller.getHorizontalScrollBar();
      int maxValue = hScroll.getMaximum() - hScroll.getBlockIncrement();
      System.out.println("scroll: " + maxValue + " cur: " + hScroll.getValue());
      if (hScroll.getValue() >= maxValue)
        hScroll.setValue(hScroll.getMaximum());
    }
		 */
		waveList.invalidate();
		//    waveList.repaint();
	}

	public boolean ZoomIn() {
		actualZoom += zoomIncrement;    
		timeHeader.repack();
		repackWaveList();
		return actualZoom != zoomLimit;
	};

	public boolean ZoomOut() {
		actualZoom -= zoomIncrement;
		timeHeader.repack();
		repackWaveList();
		return actualZoom != 0;
	};

	public void timeNotify() {
		//apelata de alecu cand are el chef.
		//niciodata, de fapt, daca n-o sa scrie odata VCDReader-ul ala.
		//scrollBar.setMaximum(++simulationTime);
		simulationTime++;
		timeHeader.repack();
		repackWaveList();
	};

	@SuppressWarnings("serial")
	class NameScrollPane extends JScrollPane {
		NameScrollPane(JComponent c) {
			super(c);
		}

		public Dimension getMaximumSize() {
			return new Dimension(120, waveHeight * waveList.getModel().getSize());
		}

		public Dimension getPreferredSize() {
			return getMaximumSize();
		}

		public Dimension getMinimumSize() {
			return new Dimension(120, 0);
		}
	}

	/**
	 * Glues the name list and the little horizontal
	 * scrollbar in the lower left corner of the main scroller.
	 */
	class WaveNameScroller implements AdjustmentListener, ChangeListener{
		JScrollBar scroll;
		JViewport nameView;
		WaveNameScroller(JScrollBar scroll, JViewport nameView) {
			this.scroll = scroll;
			this.nameView = nameView;
			scroll.addAdjustmentListener(this);
			nameView.addChangeListener(this);
		}
		//////////////////////////
		//AdjustmentListener impl.
		/////////////////////////
		/**
		 * Scrollbar notification
		 */
		public void adjustmentValueChanged(AdjustmentEvent e) {
			Point p = nameView.getViewPosition();
			p.x = scroll.getValue();
			nameView.setViewPosition(p);
		}

		//////////////////////
		//ChangeListener impl.
		//////////////////////
		/**
		 * View notif.
		 */
		public void stateChanged(javax.swing.event.ChangeEvent e) {
			scroll.setMaximum(nameView.getViewSize().width);
			scroll.setVisibleAmount(nameView.getExtentSize().width);
			scroll.setValue(nameView.getViewPosition().x);
		}
	}

	/**
	 * The time unit grdations.
	 */
	@SuppressWarnings("serial")
	public class TimeHeader extends JComponent {

		int unitWidth;

		public TimeHeader() {
			super();
			setBackground(java.awt.Color.white);
			repack();
		}

		public Dimension getPreferredSize() {
			return new Dimension((simulationTime + 2) * unitWidth, 20);
		}

		public Dimension getMinimumSize() {
			return getPreferredSize();
		}

		void repack() {
			unitWidth = waveMinimumUnitWeigth + actualZoom;
			invalidate();
			repaint();
		}

		public void paint(Graphics g) {
			System.out.println("TimeHeader.paint->");
			g.setColor(Color.white);
			g.fillRect(0, 0, getWidth(), getHeight());

			g.setColor(Color.black);
			g.drawLine(0, 19, getWidth(), 19);

			int startX = g.getClipBounds().x;

			for(int i = 0, x = 0; i <= simulationTime; x += unitWidth, i++) {
				//if (x < startX) continue;
				g.drawString(Integer.toString(i), x, 15);
				if (i % 10 == 0)
					g.drawLine(x, 17, x, 20);
				else
					g.drawLine(x, 18, x, 20);
			}
			System.out.println("TimeHeader.paint-<");
		}
	}
}
